package cs.rsa.ts14.juliet;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import cs.rsa.ts14.framework.ClassType;
import cs.rsa.ts14.framework.ReportBuilder;
import cs.rsa.ts14.standard.ClassMap;


public class CategoryOverviewBuilder implements ReportBuilder {
	
	private Map<ClassType, ReportClass> classList = new HashMap<ClassType, ReportClass>();
	
	private CategorySyntaxConstructor constructor;
	
	public CategoryOverviewBuilder(CategorySyntaxConstructor constructor) {
		this.constructor = constructor;
	}
	
	@Override
	public void buildBegin() {
		// TODO Auto-generated method stub

	}

	@Override
	public void buildWeekSpecification(int weekNo, int countWorkdays,
			int countUsedVacationdays) {
		// TODO Auto-generated method stub

	}

	@Override
	public void buildWorkSpecification(String category, String subCategory, double hours) {
		
		if(hours <= 0){ //Registrations below or 0 are ignored.
			return; 
		}
		if(subCategory != null && subCategory.length() == 0){ //If subcategory is null or empty
			return;
		}

		ReportClass rc = getClass(category);
		if(rc == null){  //If category is not recognized
			return;
		}
		
		rc.workSpecification(category, subCategory, hours);
	}


	/**
	 * Returns the assigned class for the category
	 * @param category The category
	 * @return The related ReportClass
	 */
	private ReportClass getClass(String category) {
		ClassType type = ClassMap.mapCategoryToClass(category);
		if(type == null) {
			return null;
		}
		else {
			return getClassData(type);
		}		
	}
	
	/**
	 * Gets the ReportClass from the classlist. If it does 
	 * not exist, it will be created and and added to the list.
	 * @param type The classtype
	 * @return The related ClassReport
	 */
	private ReportClass getClassData(ClassType type) {
		ReportClass c = classList.get(type);
		if(c == null) {
			String classname = ClassMap.getClassTypeAsString(type);
			ReportClass res = new ReportClass(classname);
			classList.put(type, res);
			return res;
		}
		return c;
	}
	
	

	@Override
	public void buildWeekDaySpecification(String weekDay, String transportMode) {
		// TODO Auto-generated method stub

	}

	@Override
	public void buildAssignment(String variable, double value) {
		// TODO Auto-generated method stub

	}

	@Override
	public void buildEnd() {
	}

	@Override
	public String getResult() {
		Set<ReportClass> list = new TreeSet<ReportClass>(classList.values());
		double total_hours = 0;
		double consultant_hours = 0;
		for (ReportClass rc : list) { 	//We need to get the total hours for producing percentage.
			total_hours += rc.getHours();
		}
		
		StringBuilder sb = new StringBuilder(constructor.buildHeaderLine());

		if(list.size() > 0){
			for (ReportClass rc : list) { 	//Run through classes and produce specification
				sb.append(constructor.buildClassLine(rc.getName(), rc.getHours(), (rc.getHours()/total_hours)*100));
				
				Set<Entry<String, Double>> set = rc.getCategories();
				for (Entry<String, Double> entry : set) {
					sb.append(constructor.buildCategoryLine(entry.getKey(), entry.getValue()));
				}
				
				if(rc.getName().equalsIgnoreCase(ClassMap.CLASS_CONSULENT)) { //Needed for later usage if present.
					consultant_hours = rc.getHours();
				}
			}
			
			sb.append(constructor.buildTotalLine(total_hours-consultant_hours, total_hours));
			sb.append(constructor.buildEndLine());
		}
		return sb.toString();
	}
	
	/**
	 * Wrapper class for containing hour data based on category
	 * @author madsen
	 *
	 */
	public class ReportClass implements Comparable<ReportClass> {
		private String name;
		private Map<String, Double> category_counter = new TreeMap<String, Double>();
		
		public ReportClass(String name) {
			this.name = name;
		}
		
		public String getName() {
			return name;
		}
		
		public void workSpecification(String category, String subCategory, double hours) {
			String cat_lower = category.toLowerCase();
			Double reg_hours = category_counter.get(cat_lower);
			
			if(reg_hours == null) {
				category_counter.put(cat_lower, hours);
			}
			else{
				category_counter.put(cat_lower, reg_hours + hours);
			}
		}
		
		public double getHours() {
			double res = 0;
			for(Double d : category_counter.values()) {
				res += d;
			}
			return res;
		}
		
		public Set<Entry<String, Double>> getCategories() {
			return category_counter.entrySet();
		}
		
		 @Override
		public int compareTo(ReportClass other){
			 return (name.compareTo(other.name));
		}
	}

}
